//
//  BLTimeAxis.m
//  BLTimeAxis
//
//  Created by dg on 2025/4/17.
// 依据 https://github.com/welljobs/DHTimeAxis 裁剪出来了，为了了解代码的实现。防止名字冲突重新命名。
// 支持原创

#import "BLTimeAxis.h"
#import "BLTimeAxisData.h"
#import "BLTimeAxisRule.h"
#import "BLTimeAxisDigitalDivision.h"
#import "BLTimeAxisBaseLine.h"
#import "BLTimeAxis+Appearance.h"
#import "BLTimeAxis+Dynamic.h"
#import "BLTimeAxisView.h"
#import "BLTimeAxisAppearance.h"

@interface BLTimeAxis ()

@property(nonatomic, strong) BLTimeAxisView *axisView;

@property (nonatomic, assign) NSTimeInterval tempTimeInterval;
@property(nonatomic, assign) CGFloat tempScale;

@property(nonatomic, readwrite, assign) __block NSTimeInterval currentTimeInterval;
@property(nonatomic, readwrite, assign) CGFloat currentScale;

@property(nonatomic, readwrite, assign, getter=isPaning) __block BOOL paning;
@property(nonatomic, readwrite, assign, getter=isPinching) BOOL pinching;

@property(nonatomic, strong) UIPanGestureRecognizer *panGesture;
@property(nonatomic, strong) UIPinchGestureRecognizer *pinchGesture;

@property(nonatomic, strong) BLTimeAxisRule *rule;
@property(nonatomic, strong) BLTimeAxisDigitalDivision *digital;
@property(nonatomic, strong) BLTimeAxisBaseLine *baseLine;

@property(nonatomic, strong) NSMutableArray *axisAppearanceArray;

@end

@implementation BLTimeAxis

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self setBackgroundColor:[UIColor yellowColor]];
        
        [BLTimeAxisAppearance sharedAppearance];
        
        [self configCommon];
    }
    
    return self;
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"currentTimeInterval"];
    [self removeObserver:self forKeyPath:@"currentScale"];
    [self removeObserver:self forKeyPath:@"paning"];
    [self removeObserver:self forKeyPath:@"pinching"];
    [self resignAppearanceNotification];
}


#pragma mark - config method

- (void)configCommon {
    self.backgroundColor = [UIColor yellowColor];
    
    [self addSubview:self.axisView];
    
    [self updateAppearance];
    
    [self addGestureRecognizer:self.panGesture];
    [self addGestureRecognizer:self.pinchGesture];
    
    [self addObserver:self forKeyPath:@"currentTimeInterval" options:NSKeyValueObservingOptionNew context:nil];
    [self addObserver:self forKeyPath:@"currentScale" options:NSKeyValueObservingOptionNew context:nil];
    [self addObserver:self forKeyPath:@"paning" options:NSKeyValueObservingOptionNew context:nil];
    [self addObserver:self forKeyPath:@"pinching" options:NSKeyValueObservingOptionNew context:nil];
    [self registeAppearanceNotification];
}


#pragma mark -- public method
- (void)updateWithCurrentTimeInterval:(NSTimeInterval)currentTimeInterval {
    self.currentTimeInterval = currentTimeInterval;
}

- (void)updateWithDataArray:(NSArray<BLTimeAxisData *> *)dataArray {
    __weak typeof(self) weakself = self;
    CGSize viewSize = weakself.axisView.frame.size;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [weakself updateAppearanceWithDataArray:dataArray size:viewSize];
        [weakself.axisView setDataArray:dataArray];
    });
}

- (void)manuallyStopRolling {
    // 刹车
    __weak typeof(self) weakself = self;
    [self manuallyStopRollingWithDeceleratingExtraUpdate:^{
        // 更新变量
        weakself.paning = NO;
        weakself.currentTimeInterval = weakself.rule.currentTimeInterval;
    }];
}

#pragma mark -- private method

/// 判断在停止拖动的情况下，数据数组是否存在包含当前时间的数据项
- (void)judgeExistDataInTheInterval:(NSTimeInterval)targetTimeInterval fromDataArray:(NSArray <BLTimeAxisData *> *)dataArray withPanState:(BOOL)isPaning {
    
    if (isPaning == YES || !dataArray) return;
    
    if ([dataArray count] == 0) return;
    
    __weak typeof(self) weakself = self;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i <[dataArray count]; i++) {
            BLTimeAxisData *subData = [dataArray objectAtIndex:i];
            
            if (subData.startTimeInterval <= targetTimeInterval && subData.endTimeInterval >= targetTimeInterval) {
                if (weakself.delegate && [weakself.delegate respondsToSelector:@selector(timeAxis:didEndedAtDataSection:)]) {
                    [weakself.delegate timeAxis:weakself didEndedAtDataSection:[subData copy]];
                    break;
                }
            }
        }
    });
}


/// 组装AxisView的InterfaceArray
- (void)updateAxisViewAppearanceArray {
    self.axisView.appearanceArray = [self.axisAppearanceArray copy];
}

/// 更新当前时间算法
-(void)updateCurrentTimeIntervalFrom:(NSTimeInterval)from offset:(CGPoint)offset viewSize:(CGSize)viewSize {
    CGFloat optimisticOffset = 0.0;
    CGFloat optimisticViewSize = 0.0;
    
    if (_delegate && [_delegate respondsToSelector:@selector(translationCurrentTimeIntervalFromOffset:viewSize:toOptimisticOffset:optimisticViewSize:)]) {
        [_delegate translationCurrentTimeIntervalFromOffset:offset
                viewSize:viewSize
                toOptimisticOffset:&optimisticOffset
                                         optimisticViewSize:&optimisticViewSize];
    } else {
        [self uponAppearanceForUpdateCurrentTimeIntervalFromOffset:offset
                viewSize:viewSize
                toOptimisticOffset:&optimisticOffset
                                                optimisticViewSize:&optimisticViewSize];
    }
    
    __weak typeof(self) weakself = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSTimeInterval rawInterval = from - (optimisticOffset * 1.0 / [weakself.digital aSecondOfPixelWithViewWidth:optimisticViewSize]);
        
        if (rawInterval <0) {
            rawInterval = 0;
        }
        if (rawInterval > 86400) {
            rawInterval = 86400;
        }

        weakself.currentTimeInterval = rawInterval;
    });
}

- (void)updateAppearance {
    self.axisView.backgroundColor = [self updateAppearanceMainBackgroundColor];
    self.axisView.rendererClass = [self updateAppearanceRenderer];
    
    NSArray *tmp = [self updateAppearanceArrayWithSize:self.axisView.frame.size];
    for (id<BLTimeAxisComponent> axis in tmp) {
        if ([axis isKindOfClass:[BLTimeAxisRule class]]) {
            _rule = axis;
        } else if ([axis isKindOfClass:[BLTimeAxisDigitalDivision class]]) {
            _digital = axis;
        } else if ([axis isKindOfClass:[BLTimeAxisBaseLine class]]) {
            _baseLine = axis;
        }
        
    }
    
    self.currentTimeInterval = _rule.currentTimeInterval;
    self.currentScale = _digital.currentScale;
    
    self.axisAppearanceArray = [tmp mutableCopy];
    
    [self updateAxisViewAppearanceArray];
}


#pragma mark -- observer
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"currentTimeInterval"] || [keyPath isEqualToString:@"currentScale"]) {
        _rule.currentTimeInterval = _currentTimeInterval;
        _digital.currentScale = _currentScale;
        _baseLine.currentScale = _currentScale;
        
        // 这里通知当前时间改变
        [self updateAxisViewAppearanceArray];
    }
    
    if ([keyPath isEqualToString:@"currentTimeInterval"]) {
        if (_delegate && [_delegate respondsToSelector:@selector(timeAxis:didChangedTimeInterval:)]) {
            [_delegate timeAxis:self didChangedTimeInterval:_currentTimeInterval];
        }
        
        [self judgeExistDataInTheInterval:_currentTimeInterval fromDataArray:self.axisView.dataArray
                             withPanState:self.isPaning];
    } else if ([keyPath isEqualToString:@"currentScale"]) {
        if (_delegate && [_delegate respondsToSelector:@selector(timeAxis:didChangedScale:)]) {
            [_delegate timeAxis:self didChangedScale:_currentScale];
        }
    } else if ([keyPath isEqualToString:@"paning"]) {
        if (self.isPaning) {
            if (_delegate && [_delegate respondsToSelector:@selector(timeAxisDidBeginScrolling:)]) {
                [_delegate timeAxisDidBeginScrolling:self];
            }
        } else {
            if (_delegate && [_delegate respondsToSelector:@selector(timeAxisDidEndScrolling:)]) {
                [_delegate timeAxisDidEndScrolling:self];
            }
        }
    } else if ([keyPath isEqualToString:@"pinching"]) {
        if (self.isPinching) {
            
        } else {
            
        }
    }
}

#pragma mark -- gesture

// 单手拖动
- (void)panAction:(UIPanGestureRecognizer *)sender {
    switch (sender.state) {
        case UIGestureRecognizerStateBegan: {
            self.paning = YES;
            _tempTimeInterval = _currentTimeInterval;
        }
            break;
        case UIGestureRecognizerStateChanged:{
            CGPoint point = [sender translationInView:sender.view];
            if (_currentTimeInterval == 0 && point.x < -50) {
                break;
            }
            if (_currentTimeInterval == 86400 && point.x > 50) {
                break;
            }
            [self updateCurrentTimeIntervalFrom:_tempTimeInterval
                                         offset:point
                                       viewSize:self.axisView.frame.size];
        }
            break;
        case UIGestureRecognizerStateEnded: {
            // 松手后的速度——即理论上脱手后能达到的偏移距离，乘以0.5是怕你飘了~
            CGPoint velocity = [sender velocityInView:sender.view];
            velocity.x *= 0.5;
            velocity.y *= 0.5;
            CGSize viewSize = self.axisView.frame.size;
            
            _tempTimeInterval = _currentTimeInterval;
            
            __weak typeof(self) weakself = self;
            [self deceleratingAnimateWithVelocityPoint:velocity action:^(CGPoint deceleratingSpeedPoint, BOOL stop) {
                
                if (stop) {
                    weakself.paning = NO;
                }
                
                // deceleratingSpeedPoint是衰减速度，与velocity的意思一样，它是由velocity值慢慢衰减到0的
                // 两个的差值即每隔一定时间的变化量 与 手势状态UIGestureRecognizerStateChanged反馈的效果一致
                [weakself updateCurrentTimeIntervalFrom:weakself.tempTimeInterval offset:CGPointMake(velocity.x - deceleratingSpeedPoint.x, velocity.y - deceleratingSpeedPoint.y) viewSize:viewSize];
            }];
        }
            break;
        default:
            self.paning = NO;
            break;
    }
}

/// 两手指捏合响应
- (void)pinchAction:(UIPinchGestureRecognizer *)sender {
    
    switch (sender.state) {
        case UIGestureRecognizerStateBegan:
            _tempScale = _currentScale;
            self.pinching = YES;
            break;
        case UIGestureRecognizerStateChanged:
            self.currentScale = _tempScale + sender.scale - 1;
            break;
        default:
            self.pinching = NO;
            break;
    }
}

#pragma mark - getters
- (UIPanGestureRecognizer *)panGesture {
    if (!_panGesture) {
        _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
        _panGesture.maximumNumberOfTouches = 1;
    }
    return _panGesture;
}

- (UIPinchGestureRecognizer *)pinchGesture {
    if (!_pinchGesture) {
        _pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)];
    }
    return _pinchGesture;
}

- (BLTimeAxisView *)axisView {
    if (!_axisView) {
        _axisView = [[BLTimeAxisView alloc] initWithFrame:self.bounds];
    }
    return _axisView;
}

- (NSMutableArray *)axisAppearanceArray {
    if (!_axisAppearanceArray) {
        _axisAppearanceArray = [[NSMutableArray alloc] initWithCapacity:3];
    }
    return _axisAppearanceArray;
}

- (void)setCurrentScale:(CGFloat)currentScale {
    /// 优化比例值
    [_digital updateToOptimisticScale:&currentScale];
    _currentScale = currentScale;
}

- (void)setFrame:(CGRect)frame {
    [super setFrame:frame];
    self.axisView.frame = self.bounds;
}

@end
